package org.dfzt.common.system.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dfzt.common.api.vo.Result;
import org.dfzt.common.constant.CommonConstant;
import org.dfzt.common.system.service.IUploadService;
import org.dfzt.common.util.CommonUtils;
import org.dfzt.common.util.RestUtil;
import org.dfzt.common.util.TokenUtils;
import org.dfzt.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @Author scott
 * @since 2018-12-20
 */

@RestController
@RequestMapping("/sys/common")
public class CommonController {
	private static final Log log = LogFactory.getLog(CommonController.class);
	@Autowired
	private IUploadService uploadService;

	@Value(value = "${dfzt.path.upload}")
	private String uploadpath;

	@Value(value = "${dfzt.path.download:#{null}}")
	private String downloadPath;

	/**
	 * 本地：local minio：minio 阿里：alioss
	 */
	@Value(value = "${dfzt.uploadType}")
	private String uploadType;

	/**
	 * @return
	 * @Author 政辉
	 */
	@GetMapping("/403")
	public Result<?> noauth() {
		return Result.error("没有权限，请联系管理员授权");
	}

	/**
	 * 文件上传统一方法
	 *
	 * @param request
	 * @param response
	 * @return
	 */
	@PostMapping(value = "/upload")
	public Result<?> upload(HttpServletRequest request, HttpServletResponse response) {
		Result<?> result = new Result<>();
		String savePath = "";
		String bizPath = request.getParameter("biz");
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象
		if (oConvertUtils.isEmpty(bizPath)) {
			if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
				// 未指定目录，则用阿里云默认目录 upload
				bizPath = "upload";
				// result.setMessage("使用阿里云文件上传时，必须添加目录！");
				// result.setSuccess(false);
				// return result;
			} else {
				bizPath = "";
			}
		}
		if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
			// 针对jeditor编辑器如何使 lcaol模式，采用 base64格式存储
			String jeditor = request.getParameter("jeditor");
			if (oConvertUtils.isNotEmpty(jeditor)) {
				result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL);
				result.setSuccess(true);
				return result;
			} else {
				savePath = this.uploadLocal(file, bizPath);
			}
		} else {
			savePath = uploadService.upload(file, bizPath, uploadType);
		}
		if (oConvertUtils.isNotEmpty(savePath)) {
			result.setMessage(savePath);
			result.setSuccess(true);
		} else {
			result.setMessage("上传失败！");
			result.setSuccess(false);
		}
		return result;
	}

	/**
	 * 文件上传统一方法-注册时用-无token
	 *
	 * @param request
	 * @param response
	 * @return
	 */
	@PostMapping(value = "/uploadRegistered")
	public Result<?> uploadRegistered(HttpServletRequest request, HttpServletResponse response) {
		Result<?> result = new Result<>();
		String savePath = "";
		String bizPath = request.getParameter("biz");
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象
		if (oConvertUtils.isEmpty(bizPath)) {
			if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
				// 未指定目录，则用阿里云默认目录 upload
				bizPath = "upload";
				// result.setMessage("使用阿里云文件上传时，必须添加目录！");
				// result.setSuccess(false);
				// return result;
			} else {
				bizPath = "";
			}
		}
		if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
			// 针对jeditor编辑器如何使 lcaol模式，采用 base64格式存储
			String jeditor = request.getParameter("jeditor");
			if (oConvertUtils.isNotEmpty(jeditor)) {
				result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL);
				result.setSuccess(true);
				return result;
			} else {
				savePath = this.uploadLocal(file, bizPath);
			}
		} else {
			savePath = uploadService.upload(file, bizPath, uploadType);
		}
		if (oConvertUtils.isNotEmpty(savePath)) {
			result.setMessage(savePath);
			result.setSuccess(true);
		} else {
			result.setMessage("上传失败！");
			result.setSuccess(false);
		}
		return result;
	}

	/**
	 * 多文件上传统一方法
	 *
	 * @param request
	 * @param response
	 * @return
	 */
	@PostMapping(value = "/uploadList")
	public Result<List<String>> uploadList(HttpServletRequest request, HttpServletResponse response) throws Exception {
		Result<List<String>> result = new Result<>();
		List<String> savePathList = new ArrayList<>();
		String bizPath = request.getParameter("biz");

		// 创建一个通用的多部分解析器
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
				request.getSession().getServletContext());
		// 判断 request 是否有文件上传,即多部分请求
		if (multipartResolver.isMultipart(request)) {
			MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
			// 获取上传的多文件对象
			MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();
			List<MultipartFile> fileSet = new LinkedList<>();
			for (Map.Entry<String, List<MultipartFile>> temp : multiFileMap.entrySet()) {
				fileSet = temp.getValue();
			}

			for (MultipartFile file : fileSet) {
				String savePath = "";
				// 以下内容与单文件上传相同
				if (oConvertUtils.isEmpty(bizPath)) {
					if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
						// 未指定目录，则用阿里云默认目录 upload
						bizPath = "upload";
						// result.setMessage("使用阿里云文件上传时，必须添加目录！");
						// result.setSuccess(false);
						// return result;
					} else {
						bizPath = "";
					}
				}
				if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
					// 针对jeditor编辑器如何使 lcaol模式，采用 base64格式存储
					String jeditor = request.getParameter("jeditor");
					if (oConvertUtils.isNotEmpty(jeditor)) {
						result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL);
						result.setSuccess(true);
						return result;
					} else {
						savePath = this.uploadLocal(file, bizPath);
					}
				} else {
					savePath = uploadService.upload(file, bizPath, uploadType);
				}
				savePathList.add(savePath);
			}
		}
		if (savePathList.size() != 0) {
			result.setResult(savePathList);
			result.setSuccess(true);
		} else {
			result.setMessage("上传失败！");
			result.setSuccess(false);
		}
		return result;
	}

	/**
	 * 删除文件
	 *
	 * @param params
	 * @return
	 */
	@PostMapping(value = "/removeFiles")
	public Result<?> removeFiles(@RequestBody JSONObject params) {
		Result<?> result = new Result<>();
		String bizPath = params.getString("biz");
		String fileName = params.getString("fileName");
		if (fileName == null || "".equals(fileName)) {
			result.setMessage("文件名为空！");
			result.setSuccess(false);
			return result;
		} else if (bizPath == null || "".equals(bizPath)) {
			result.setMessage("文件路径为空！");
			result.setSuccess(false);
			return result;
		}
		// 目前只支持uploadType=minio的文件删除，删除多文件时，文件名用逗号隔开
		if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
			String[] fileNameList = fileName.split(",");
			for (int i = 0; i < fileNameList.length; i++) {
				String name = fileNameList[i];
				uploadService.removeFile(name, bizPath);
			}
		} else {
			result.setMessage("文件暂不能删除！");
			result.setSuccess(false);
		}
		return result;
	}

	/**
	 * minio获取文件外链
	 *
	 * @param fileName
	 * @param bizPath
	 * @param expires
	 * @return
	 */
	@GetMapping(value = "/getObjectURL")
	public Result<String> getObjectURL(@RequestParam("fileName") String fileName,
			@RequestParam("bizPath") String bizPath, @RequestParam("expires") Integer expires) {
		Result<String> result = new Result<>();
		if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
			if (expires > 7 * 24 * 60 * 60) {
				expires = 7 * 24 * 60 * 60;
			}
			String url = uploadService.getObjectURL(fileName, bizPath, expires);
			result.setResult(url);
		} else {
			result.setMessage("文件暂不能预览！");
			result.setSuccess(false);
		}

		return result;
	}

	/**
	 * minio下载文件
	 *
	 * @param fileName
	 * @param bizPath
	 * @param response
	 */
	@GetMapping(value = "/downloadFile")
	public void downloadFile(@RequestParam("fileName") String fileName, @RequestParam("bizPath") String bizPath,
			HttpServletResponse response) {
		if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
			InputStream inputStream = uploadService.getMinioFile(fileName, bizPath);
			OutputStream outputStream = null;
			try {
				// 保存到本地文件
//				int index;
//				byte[] bytes = new byte[1024];
//				FileOutputStream downloadFile = new FileOutputStream("D://"+fileName);
//				while ((index = inputStream.read(bytes)) != -1) {
//					downloadFile.write(bytes, 0, index);
//					downloadFile.flush();
//				}
//				downloadFile.close();
//				inputStream.close();

				outputStream = new BufferedOutputStream(response.getOutputStream());
				// 创建存放文件内容的数组
				byte[] buff = new byte[1024];
				// 所读取的内容使用n来接收
				int n;
				// 当没有读取完时,继续读取,循环
				while ((n = inputStream.read(buff)) != -1) {
					// 将字节数组的数据全部写入到输出流中
					outputStream.write(buff, 0, n);
				}
				// 强制将缓存区的数据进行输出
				outputStream.flush();

			} catch (Exception e) {
				log.error("文件读取异常", e);
			} finally {
				if (outputStream != null) {
					try {
						// 关流
						outputStream.close();
						inputStream.close();

					} catch (IOException e) {
						log.error("文件流关闭异常", e);
					}
				}
			}
		}
	}

	/**
	 * 多文件打包下载
	 * 
	 * @param fileName
	 * @param bizPath
	 * @param response
	 */
	@GetMapping(value = "/downloadFileZip")
	public void downloadFileZip(@RequestParam("fileName") String fileName, @RequestParam("bizPath") String bizPath,
			HttpServletResponse response) {
		if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
			OutputStream out = null;
			ZipOutputStream zos = null;
			BufferedInputStream bis = null;
			File zip = null;
			try {
				String[] fileNames = fileName.split(",");

				// 1.将附件中多个文件进行压缩，批量打包下载文件
				// 创建压缩文件需要的空的zip包
				Date date = new Date();
				String zipName = "temp_" + date.getTime() + ".zip";
				String zipBasePath = downloadPath;
				String zipFilePath = zipBasePath + File.separator + zipName;
				// 创建文件夹
				File fileDir = new File(zipBasePath);
				if (!fileDir.exists()) {
					fileDir.mkdirs();
				}
				// 创建压缩文件
				zip = new File(zipFilePath);
				if (!zip.exists()) {
					zip.createNewFile();
				}
				// 创建zip文件输出流
				zos = new ZipOutputStream(new FileOutputStream(zip));
				// 将文件依次写入zip流
				this.zipFile(bizPath, fileNames, zos);
				// 在返回respones之前手动关闭zip文件流
				zos.close();

				// 2.IO流实现下载的功能
				response.setContentType("text/html; charset=UTF-8"); // 设置编码字符
				response.setContentType("application/octet-stream"); // 设置内容类型为下载类型
				response.setHeader("Content-disposition", "attachment;filename=" + zipName);// 设置下载的压缩文件名称
				out = response.getOutputStream(); // 创建页面返回方式为输出流，会自动弹出下载框

				// 将打包后的文件写到客户端，使用缓冲流输出
				bis = new BufferedInputStream(new FileInputStream(zipFilePath));
				byte[] buff = new byte[bis.available()];
				bis.read(buff);
				out.write(buff);// 输出数据文件
				out.flush();// 释放缓存

			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					bis.close();// 关闭缓冲流
					out.close();// 关闭输出流
					zip.delete();// 删除创建的本地文件

				} catch (IOException e) {
					log.error("文件流关闭异常", e);
				}
			}

		}

	}

	/**
	 * 压缩文件
	 * 
	 * @param bizPath
	 * @param fileNames
	 * @param zos
	 * @throws IOException
	 */
	private void zipFile(String bizPath, String[] fileNames, ZipOutputStream zos) throws IOException {

		// 循环读取文件路径集合，获取每一个文件的路径
		for (int i = 0; i < fileNames.length; i++) {
			String name = fileNames[i];
			InputStream bis = uploadService.getMinioFile(name, bizPath);

			// 将文件写入zip内，即将文件进行打包
			zos.putNextEntry(new ZipEntry(name));

			// 写入文件的方法，同上
			int size = 0;
			byte[] buffer = new byte[1024]; // 设置读取数据缓存大小
			while ((size = bis.read(buffer)) > 0) {
				zos.write(buffer, 0, size);
			}
			// 关闭输入输出流
			zos.closeEntry();
			bis.close();
		}
	}

	/**
	 * 本地文件上传
	 *
	 * @param mf      文件
	 * @param bizPath 自定义路径
	 * @return
	 */
	private String uploadLocal(MultipartFile mf, String bizPath) {
		try {
			String ctxPath = uploadpath;
			String fileName = null;
			File file = new File(ctxPath + File.separator + bizPath + File.separator);
			if (!file.exists()) {
				file.mkdirs();// 创建文件根目录
			}
			String orgName = mf.getOriginalFilename();// 获取文件名
			orgName = CommonUtils.getFileName(orgName);
			if (orgName.indexOf(".") != -1) {
				fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis()
						+ orgName.substring(orgName.indexOf("."));
			} else {
				fileName = orgName + "_" + System.currentTimeMillis();
			}
			String savePath = file.getPath() + File.separator + fileName;
			File savefile = new File(savePath);
			FileCopyUtils.copy(mf.getBytes(), savefile);
			String dbpath = null;
			if (oConvertUtils.isNotEmpty(bizPath)) {
				dbpath = bizPath + File.separator + fileName;
			} else {
				dbpath = fileName;
			}
			if (dbpath.contains("\\")) {
				dbpath = dbpath.replace("\\", "/");
			}
			return dbpath;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return "";
	}

	/**
	 * 预览图片&下载文件
	 * 请求地址：http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
	 *
	 * @param request
	 * @param response
	 */
	@GetMapping(value = "/static/**")
	public void view(HttpServletRequest request, HttpServletResponse response) {
		// ISO-8859-1 ==> UTF-8 进行编码转换
		String imgPath = extractPathFromPattern(request);
		if (oConvertUtils.isEmpty(imgPath) || imgPath == "null") {
			return;
		}
		// 其余处理略
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			imgPath = imgPath.replace("..", "");
			if (imgPath.endsWith(",")) {
				imgPath = imgPath.substring(0, imgPath.length() - 1);
			}
			String filePath = uploadpath + File.separator + imgPath;
			File file = new File(filePath);
			if (!file.exists()) {
				response.setStatus(404);
				throw new RuntimeException("文件不存在..");
			}
			response.setContentType("application/force-download");// 设置强制下载不打开
			response.addHeader("Content-Disposition",
					"attachment;fileName=" + new String(file.getName().getBytes("UTF-8"), "iso-8859-1"));
			inputStream = new BufferedInputStream(new FileInputStream(filePath));
			outputStream = response.getOutputStream();
			byte[] buf = new byte[1024];
			int len;
			while ((len = inputStream.read(buf)) > 0) {
				outputStream.write(buf, 0, len);
			}
			response.flushBuffer();
		} catch (IOException e) {
			log.error("预览文件失败" + e.getMessage());
			response.setStatus(404);
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					log.error(e.getMessage(), e);
				}
			}
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					log.error(e.getMessage(), e);
				}
			}
		}

	}

	/**
	 * 下载文件
	 * 请求地址：http://localhost:8080/common/download/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
	 *
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@GetMapping(value = "/download/**")
	public void download(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// ISO-8859-1 ==> UTF-8 进行编码转换
		String filePath = extractPathFromPattern(request);
		// 其余处理略
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			filePath = filePath.replace("..", "");
			if (filePath.endsWith(",")) {
				filePath = filePath.substring(0, filePath.length() - 1);
			}
			String localPath = uploadpath;
			String downloadFilePath = localPath + File.separator + filePath;
			File file = new File(downloadFilePath);
			if (file.exists()) {
				response.setContentType("application/force-download");// 设置强制下载不打开            
				response.addHeader("Content-Disposition",
						"attachment;fileName=" + new String(file.getName().getBytes("UTF-8"), "iso-8859-1"));
				inputStream = new BufferedInputStream(new FileInputStream(file));
				outputStream = response.getOutputStream();
				byte[] buf = new byte[1024];
				int len;
				while ((len = inputStream.read(buf)) > 0) {
					outputStream.write(buf, 0, len);
				}
				response.flushBuffer();
			}

		} catch (Exception e) {
			log.info("文件下载失败" + e.getMessage());
			// e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * 综合报告-doc文档下载
	 */
	@GetMapping(value = "/downloadDoc/**")
	public void downloadDoc(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// ISO-8859-1 ==> UTF-8 进行编码转换
		String filePath = request.getParameter("filePath");
		// 其余处理略
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			File file = new File(filePath);
			if (file.exists()) {
				// 以流的形式下载文件。
				response.setContentType("application/force-download");// 设置强制下载不打开            
				response.addHeader("Content-Disposition",
						"attachment;fileName=" + new String(file.getName().getBytes("UTF-8"), "iso-8859-1"));
				inputStream = new BufferedInputStream(new FileInputStream(file));
				outputStream = response.getOutputStream();
				byte[] buf = new byte[1024];
				int len;
				while ((len = inputStream.read(buf)) > 0) {
					outputStream.write(buf, 0, len);
				}
				response.flushBuffer();
			}

		} catch (Exception e) {
			log.info("文件下载失败" + e.getMessage());
			// e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * @param modelAndView
	 * @return
	 * @功能：pdf预览Iframe
	 */
	@RequestMapping("/pdf/pdfPreviewIframe")
	public ModelAndView pdfPreviewIframe(ModelAndView modelAndView) {
		modelAndView.setViewName("pdfPreviewIframe");
		return modelAndView;
	}

	/**
	 * 把指定URL后的字符串全部截断当成参数 这么做是为了防止URL中包含中文或者特殊字符（/等）时，匹配不了的问题
	 *
	 * @param request
	 * @return
	 */
	private static String extractPathFromPattern(final HttpServletRequest request) {
		String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
		String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
		return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
	}

	/**
	 * 中转HTTP请求，解决跨域问题
	 *
	 * @param url 必填：请求地址
	 * @return
	 */
	@RequestMapping("/transitRESTful")
	public Result transitRESTful(@RequestParam("url") String url, HttpServletRequest request) {
		try {
			ServletServerHttpRequest httpRequest = new ServletServerHttpRequest(request);
			// 中转请求method、body
			HttpMethod method = httpRequest.getMethod();
			JSONObject params;
			try {
				params = JSON.parseObject(JSON.toJSONString(httpRequest.getBody()));
			} catch (Exception e) {
				params = new JSONObject();
			}
			// 中转请求问号参数
			JSONObject variables = JSON.parseObject(JSON.toJSONString(request.getParameterMap()));
			variables.remove("url");
			// 在 headers 里传递Token
			String token = TokenUtils.getTokenByRequest(request);
			HttpHeaders headers = new HttpHeaders();
			headers.set("X-Access-Token", token);
			// 发送请求
			String httpURL = URLDecoder.decode(url, "UTF-8");
			ResponseEntity<String> response = RestUtil.request(httpURL, method, headers, variables, params,
					String.class);
			// 封装返回结果
			Result<Object> result = new Result<>();
			int statusCode = response.getStatusCodeValue();
			result.setCode(statusCode);
			result.setSuccess(statusCode == 200);
			String responseBody = response.getBody();
			try {
				// 尝试将返回结果转为JSON
				Object json = JSON.parse(responseBody);
				result.setResult(json);
			} catch (Exception e) {
				// 转成JSON失败，直接返回原始数据
				result.setResult(responseBody);
			}
			return result;
		} catch (Exception e) {
			log.debug("中转HTTP请求失败", e);
			return Result.error(e.getMessage());
		}
	}

}
